#include "05_TCPconnection.hpp"
#include "06_EventLoop.hpp" // 在实现文件中包含头文件
#include <stdlib.h>
#include <sys/socket.h>     // socket | bind | connect | listen | accept | send | recv
#include <unistd.h>         // close 因为还能关闭文件和管道，所以独自升级了
#include <errno.h>
#include <cstring>
#include <iostream>
#include <sstream>

inline void error_msg(const char* msg = "", int ret_flag = -1)
{
    char buff[256] = {0};
    snprintf(buff, sizeof(buff), "%s | %d | %s : %s", __FILE__, __LINE__, __FILE__, msg);
    perror(buff);
}

namespace xy
{
TCPconnection::TCPconnection(int fd, Event_Loop* loop)
    :_sock(fd)
    ,_sockIO(fd)
    ,_localAddr(get_local_address())
    ,_peerAddr(get_peer_address())
    ,_loop(loop)
    ,_is_closed(false)
    ,_msg_cache("")
    {
        //设置非阻塞模式（但是我并没有这个模式）
        //_sock.setnonblock();
    }

InetAddress TCPconnection::get_local_address()
{
    // 这么写我也不晓得返回了什么...
    //return _localAddr;
    
    struct sockaddr_in temp_addr;
    //memset(temp_addr, 0, sizeof(temp_addr));    // memset的第一个参数需要指针
    memset(&temp_addr, 0, sizeof(temp_addr));     // memset定义在cstring中
    
    // 第三个参数需要socklen_t*类型，用于传入传出地址长度，应添加地址符并显示转换类型
    //int ret = getsockname(_sock.get_fd(), (struct sockaddr*)&temp_addr, sizeof(temp_addr)); }
    socklen_t len = sizeof(temp_addr);    
    int ret = getsockname(_sock.get_fd(), (struct sockaddr*)&temp_addr, &len); 

    if(ret == -1)
    {
        error_msg("getsockname error");
        exit(EXIT_FAILURE);
    }

    return InetAddress(temp_addr);  // 此处需要InetAddress(struct sockadd_in构造函数)
}

InetAddress TCPconnection::get_peer_address()
{
    struct sockaddr_in temp_addr;
    memset(&temp_addr, 0, sizeof(temp_addr));     // memset定义在cstring中
    
    socklen_t len = sizeof(temp_addr);    
    int ret = getpeername(_sock.get_fd(), (struct sockaddr*)&temp_addr, &len); 

    if(ret == -1)
    {
        error_msg("getpeername error");
        exit(EXIT_FAILURE);
    }

    return InetAddress(temp_addr);  // 此处需要InetAddress(struct sockadd_in构造函数)
}

void TCPconnection::send(const std::string& msg)
{
    if(msg.size() > 0)
    {
        _sockIO.sendn(msg.c_str(), msg.size());
    }
}

// 负责从套接字读取原始字节数据，并将其存储到缓存中
int TCPconnection::receive()
{
    char buff[64 * 1024] = {0};         // 栈上开辟64K字节
    int nread = _sockIO.read_line(buff, sizeof(buff));
    if(nread > 0 || (nread == -1 && errno == EINTR))
    {
        _msg_cache.append(buff, nread);   // 将读到的数据追加到缓存
        return nread;
    }
    else if(nread == 0)
    {
        //_is_close = true;
        return 0;
    }
    else if(nread == -1)
    {
        error_msg("receive");
        exit(EXIT_FAILURE);
        return -1;
    }
    return 0;   // 添加默认返回值
}

void TCPconnection::shutdown()
{
    close(_sock.get_fd());
}

bool TCPconnection::is_closed()
{
    return _sockIO.recv_peek() == 0;
}

// 仅仅打印就违反了to_string()的设定，此处使用sstream
std::string TCPconnection::tostring() const
{
#if 0
    printf("The information of TCPconnection: \n");
    printf("sockfd: %d\n", _sock.get_fd());
    printf("local_addr: %s\n", _localAddr.get_ip().c_str());
    printf("local_port: %d\n", _localAddr.get_port());
    printf("peer_addr: %s\n", _peerAddr.get_ip().c_str());
    printf("peer_port: %d\n", _peerAddr.get_port());
#endif

    std::stringstream ss;
    ss << "The information of TCPconnection: \n";
    ss << "sockfd: " << _sock.get_fd() << "\n";
    ss << "local_addr: " << _localAddr.get_ip() << "\n";
    ss << "local_port: " << _localAddr.get_port() << "\n";
    ss << "peer_addr: " << _peerAddr.get_ip() << "\n";
    ss << "peer_port: " << _peerAddr.get_port() << "\n";

    return ss.str();
}

int TCPconnection::get_fd()
{
    return _sock.get_fd();
}

// 4: 触发回调函数 | 特定时间发生时，调用之前注册的回调函数，就像菜做好了，服务员通知顾客取餐 
void TCPconnection::handle_connection_callback()
{
    // 这个方法通常不需要实现复杂逻辑，因为连接建立时的回调是在EventLoop中触发的
    // return 0;
    if (_on_connection_cb)
    {
        // 编译器无法确定 shared_from_this() 两种解决方式
        // 方案一：使用this->指针明确作用域，不过编译器会报错no member named 'shared_from_this' in 'xy::TCPconnection'
        //_on_connection_cb(this->shared_from_this());
        // 方案二：完整限定名
        _on_connection_cb(this->shared_from_this());
    }
}

// 负责从缓存中解析和处理完整消息
void TCPconnection::handle_message_callback()
{
    // 检查是否有完整的消息（以'\n'分割）
    size_t pos = _msg_cache.find('\n');
    while(pos != std::string::npos)
    {
        // 提取一条完整的消息
        std::string message = _msg_cache.substr(0, pos);
        // 移除已处理的消息
        _msg_cache.erase(0, pos + 1);
        
        // 调用用户注册的消息回调函数
        if (_on_message_cb)
        {
            // 编译器报错：call to non-static member function without an object argument
            //_on_message_cb(std::enable_shared_from_this<xy::TCPconnection>::shared_from_this(), message);
            // no member named 'shared_from_this' in 'xy::TCPconnection'
            _on_message_cb(this->shared_from_this(), message);
        }

        // 查找下一条消息
        pos = _msg_cache.find('\n');
    }
}

void TCPconnection::handle_close_callback()
{
    // 关闭连接
    if(!_is_closed)
    {
        _is_closed = true;
        
        // 使用_loop指针调用成员函数
        // 问题1：del_..函数在class Event_Loop中是私有，我是应该改为公有还是增加友元（我想增加友元，但是好像还是会报错）？
        // 保持函数私有，声明为友元类即可 
        _loop->del_epoll_read_event(_sock.get_fd());            
        
        // 问题2：_sock的析构函数里有close，这里是不是就不用写了
        // 回答：理论上不需要重复调用，但显示调用close时一个好习惯，而且多次调用close是安全的
        close(_sock.get_fd());        
    }
    
    // 问题3：_on_close的信号在Event_Loop中，我是通过数据成员_loop的指针调用，还是函数增加参数，或者在Tcpconnection数据成员中增加_on_close，通过EventLoop传递好一些呢？
    // 回答：不应该通过_loop指针调用Event_Loop中的回调，TCPconnection应该有自己的回调成员变量，这些回调应该在创建TCPconnection时由Event_Loop设置
    if(_on_close_cb){
        // 编译器报错：call to non-static member function without an object argument
        _on_close_cb(this->shared_from_this());
    }
}

}


